#include "AsyncLogging.h"

#include "CountDownLatch.h"
#include "FixedBuffer.h"
#include "LogFile.h"
#include "mutex.h"

#include <cassert>
#include <cstddef>
#include <functional>
#include <mutex>
namespace droneswarm
{
    AsyncLogging::AsyncLogging( std::string logFileName_, int flushInterval )
        : thread_( std::bind( &AsyncLogging::threadFunc, this ), "Logging" )
        , mutex_()
        , latch_( 1 )
        , basename_( logFileName_ )
        , flushInterval_( flushInterval )
        , running_( false )
        , currentBuffer_( new FixedBuffer< kLargeBuffer > )
        , nextBuffer_( new FixedBuffer< kLargeBuffer > )
        , buffers_()
    {
        assert( logFileName_.size() > 1 );
        //初始化两个缓冲区
        currentBuffer_->bzero();
        nextBuffer_->bzero();
        //将vector的容量变成16
        buffers_.reserve( 16 );
    }
    AsyncLogging::~AsyncLogging() {
        
    }
    //往缓冲区中写入数据
    void AsyncLogging::append( const char *logline, int len )
    {
        ScopedLockImpl< std::mutex > lock( mutex_ );
        //如果当前缓冲区可写入长度为len的数据，则将数据写入到当前缓冲区中
        if ( currentBuffer_->avail() > len )
        {
            //将数据写入缓冲区
            currentBuffer_->append( logline, len );
        }
        else
        {
            //当前缓冲区不足以写入长度为len的数据，则将当前缓冲区放入缓冲区容器中
            buffers_.push_back(
                currentBuffer_ );  //这段代码将复制一个shared_ptr对象，引用计数加1，所以currentBuffer_的引用计数加1。
            //将指向当前缓冲区的智能指针置为空。
            currentBuffer_.reset();
            //判断下一个缓冲区的指针是否不为空
            if ( nextBuffer_ )
            {
                //将预备缓冲区的指针赋值给当前缓冲区
                currentBuffer_ = std::move( nextBuffer_ );
            }
            else
            {
                //如果下一个缓冲区的指针为空，则重新创建一个缓冲区
                currentBuffer_.reset( new FixedBuffer< kLargeBuffer > );
            }
            //将数据写入到当前缓冲区
            currentBuffer_->append( logline, len );
            //通知已经写入了数据
            cond_.notify_one();
        }
    }

    //会有一个专门的线程用来处理从缓冲区中写数据到文件中
    void AsyncLogging::threadFunc()
    {
        //首先断言写数据到文件的线程是否启动
        assert( running_ == true );  // assert里面的条件为真时，断言不做任何事，只有为假时才中断程序
        //线程运行了此函数，因此唤醒start函数
        latch_.countDown();
        LogFile output( basename_ );
        //定义两个缓冲区，一个用来写入数据，一个用来交换
        std::shared_ptr< FixedBuffer< kLargeBuffer > > newBuffer1( new FixedBuffer< kLargeBuffer > );
        std::shared_ptr< FixedBuffer< kLargeBuffer > > newBuffer2( new FixedBuffer< kLargeBuffer > );
        //定义一个缓冲区容器
        std::vector< std::shared_ptr< FixedBuffer< kLargeBuffer > > > buffersToWrite;
        //将缓冲区容器的容量设置为16
        buffersToWrite.reserve( 16 );
        while ( running_ )
        {
            //判断这两个缓冲区是否存在且长度为0
            assert( newBuffer1 && newBuffer1->length() == 0 );
            assert( newBuffer2 && newBuffer2->length() == 0 );
            assert( buffersToWrite.empty() );
            {
                std::unique_lock< std::mutex > lock( mutex_ );
                //如果缓冲区容器为空，则等待
                if ( buffers_.empty() )
                {
                    cond_.wait_for( lock, std::chrono::seconds( flushInterval_ ) );
                }
                //将当前缓冲区放入缓冲区容器中
                buffers_.push_back( currentBuffer_ );
                //释放当前缓冲区的指针，这个指针不在指向任何对象
                currentBuffer_.reset();
                //将上面的指针指向一个新的预备缓冲区
                currentBuffer_ = std::move( newBuffer1 );  //这是所有权转移
                //将缓冲区容器的内容交换给buffersToWrite
                buffersToWrite.swap( buffers_ );
                //判断预备缓冲区nextbuffer是否为空
                if ( !nextBuffer_ )
                {
                    //如果为空，说明没有预备缓冲区可用，则将预备缓冲区指向一个新的缓冲区
                    nextBuffer_ = std::move( newBuffer2 );
                }
            }  //这段代码的目的就是将缓冲区写入容器后，交换缓冲区的内容，然后将两个缓冲区的智能指针指向新的缓冲区

            //断言写缓冲区不为空
            assert( !buffersToWrite.empty() );

            //如果缓冲区容器的大小大于25，则删除掉后面的23个缓冲区，仅保留前两个缓冲区
            if ( buffersToWrite.size() > 25 )
            {
                buffersToWrite.erase( buffersToWrite.begin() + 2, buffersToWrite.end() );
            }
            //遍历缓冲区容器，将缓冲区中的数据写入到文件中
            for ( size_t i = 0; i < buffersToWrite.size(); ++i )
            {
                //缓冲区的本质就是一个char数组，所以可以直接将数据写入到文件中
                output.append( buffersToWrite[ i ]->data(), buffersToWrite[ i ]->length() );
            }

            //写完之后仅保留两个缓冲区再容器里面，这么做的目的是为了减少内存的使用，这两个缓冲区会被重复利用，一个为当前缓冲区，一个为预备缓冲区
            if ( buffersToWrite.size() > 2 )
            {
                //保留前两个缓冲区的目的是为了复用newBuffer1和newBuffer2。
                buffersToWrite.resize( 2 );
            }
            //复用之前创建的newBuffer1和newBuffer2，这样做可以减少内存的分配
            if ( !newBuffer1 )
            {
                //断言buffersToWrite容器不为空
                assert( !buffersToWrite.empty() );
                //从容器中取出一个缓冲区给newBuffer1用
                newBuffer1 = buffersToWrite.back();
                //将使用了的这个缓冲区从容器中删除
                buffersToWrite.pop_back();
                //初始化缓冲区中的数据，该reset不是智能指针std::shared_ptr的reset，而是FixedBuffer中的reset
                newBuffer1->reset();
            }
            if ( !newBuffer2 )
            {
                //断言buffersToWrite容器不为空
                assert( !buffersToWrite.empty() );
                //从容器中取出一个缓冲区给newBuffer2用
                newBuffer2 = buffersToWrite.back();
                //将使用了的这个缓冲区从容器中删除
                buffersToWrite.pop_back();
                //初始化缓冲区中的数据
                newBuffer2->reset();
            }
            //清空缓冲区容器
            buffersToWrite.clear();
            //执行完一次循环就必须保存数据到日志文件中，不能只是存在于操作系统缓冲区中。
            output.flush();
        }
        running_ = false;
        output.flush();
    }
}  // namespace droneswarm